package com.qfmy.common.utils;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

/**
 * @ClassName: VerifyUtils
 * @author： zhengjt
 * @data： 2022-11-24 15:04:34
 * @version：1.0
 * @描述： 验证工具类，暂定放置 生成验证码图片等验证相关操作
 */

public class VerifyUtils {

    public static void main(String[] args) {
//        String verCode =CommonUtils.getVerCodeMixed(4);
//        System.out.println("验证码 == "+verCode);
//        String imageStr = createByGraphics(verCode);
//        System.out.println(imageStr);
        Map<String, String> result = createVerifyCodeImage(2, null, 100, 50);
        System.out.println(result);
    }

    /**
     * 默认宽度
     */
    private static Integer width = 100;

    /**
     * 默认高度
     */
    private static Integer height = 50;

    /**
     * 随机对象
     */
    private static Random random = new Random();

    public static Map<String, String> createVerifyCodeImage(Integer type, String verCode, Integer newWidth, Integer newHeight){
        Map<String, String> result = new HashMap<>(2);

        if (newWidth != null){
            width = newWidth;
        }
        if (newHeight != null){
            height = newHeight;
        }

        if (verCode == null || "".equals(verCode)){
            verCode = createCode(4);
        }

        result.put("verCode", verCode);
        String imgBase64 = null;


        switch (type){
            case 1 :
                imgBase64 = createByGraphics(verCode);
                break;
            case 2 :
                imgBase64 = createByGraphics2D(verCode);
                break;
            default:
                //  没有匹配值时执行
                imgBase64 = createByGraphics2D(verCode);
        }

        result.put("imageStr", imgBase64);

        return result;
    }

    /**
     * 生成验证码图片
     * @param verCode   验证码
     * @return  base64图片流
     */
    private static String createByGraphics(String verCode){
        //  图片对象
        String imageStr = null;

        //  BufferedImage,用于生成图片类
        BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);

        //  获得绘图对象
        Graphics graphics = bufferedImage.getGraphics();

        //  设置矩阵
        graphics.fillRect(0, 0, width, height);
        //  字体对象（字体类型， 字体？， 字体大小）
        Font font = new Font("Times New Roman", Font.PLAIN, (height - 2));
        //  设置字体
        graphics.setFont(font);
        //  设置颜色
        graphics.setColor(Color.BLACK);

        //  干扰线数量
        int lineNum = verCode.length();

        //  绘制干扰线，防止机器识别
        for (int i = 0; i < lineNum; i++) {
            graphics.setColor(randomColor());
            graphics.drawLine(
                    random.nextInt(width),
                    random.nextInt(height),
                    random.nextInt(width),
                    random.nextInt(height)
            );
        }
        //  添加噪点
        for (int i = 0; i < (lineNum * 2); i++) {
            graphics.setColor(randomColor());
            graphics.fillRect(
                    random.nextInt(width),
                    random.nextInt(height),
                    2,
                    2
            );
        }
        //  绘制验证码
        drawRotateCode(graphics, verCode, random);
        //  释放绘图资源
        graphics.dispose();

        //  转base64输出
        try {
            /// 测试图片生成问题 （PS：图片可正常生成，正常输出）
            // ImageIO.write(bufferedImage, "png", new File("F:\\文档资料\\图片区\\images\\ver.png"));

            //  流对象，用于存放图片
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            //  图片输出至流对象
            ImageIO.write(bufferedImage, "png", outputStream);
            //  图片流转base64字符串
            imageStr = Base64.getEncoder().encodeToString(outputStream.toByteArray());
            //  闭流
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return imageStr;
    }

    private static String createByGraphics2D(String verCode){
        //  图片对象
        String imageStr = null;

        //  BufferedImage,用于生成图片类
        BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);

        //  获得绘图对象
        Graphics2D graphics = bufferedImage.createGraphics();

        //  设置颜色
//        graphics.setColor(Color.BLACK);
        //  设置矩阵
        graphics.fillRect(0, 0, width, height);
        graphics.setColor(Color.BLACK);
        graphics.drawRect(0, 0, width-1, height-1);
        //  设置字体
        graphics.setFont(getFont());

        //  干扰线数量
        int lineNum = verCode.length();

        //  绘制干扰线，防止机器识别
        for (int i = 0; i < lineNum; i++) {
            drawLine(graphics);
        }

        //  添加噪点
        for (int i = 0; i < (lineNum * 2); i++) {
            drawPoint(graphics);
        }

        //  绘制验证码
        drawCode(graphics, verCode);

        //  释放绘图资源
        graphics.dispose();

        //  转base64输出
        try {
            /// 测试图片生成问题 （PS：图片可正常生成，正常输出）
            // ImageIO.write(bufferedImage, "png", new File("F:\\文档资料\\图片区\\images\\ver.png"));

            //  流对象，用于存放图片
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            //  图片输出至流对象
            ImageIO.write(bufferedImage, "png", outputStream);
            //  图片流转base64字符串
            imageStr = Base64.getEncoder().encodeToString(outputStream.toByteArray());
            //  闭流
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return imageStr;
    }

    /**
     * 绘制旋转倾斜验证码
     *  TODO 该方法似乎存在一些问题，部分字符下沉太多，无法完整显示。
     * @param g Graphics对象
     * @param verCode   需要绘制的验证码
     * @param random    Random对象
     */
    private static void drawRotateCode(Graphics g, String verCode, Random random){
        Graphics2D graphics = (Graphics2D) g;
        int codeCount = verCode.length();
        int charWidth = width / (codeCount + 2);
        int charHeight = height - 5;
        //  随机产生codeCount个字符的验证码。
        for (int i = 0; i < codeCount; i++) {
            int x = (i + 1) * charWidth;
            String strRandom = String.valueOf(verCode.charAt(i));
            graphics.setColor(randomColor());
            //  设置字体旋转角度，角度小于30度
            int degree = random.nextInt() % 30;
            //  正向旋转
            graphics.rotate(degree * Math.PI / 180, x, 45);
            //  绘制验证码
            graphics.drawString(strRandom, x, charHeight);
            //  反向旋转
            graphics.rotate(-degree * Math.PI / 180, x, 45);
        }
    }

    /**
     * 获取字体对象
     * @return Font对象
     */
    private static Font getFont(){
        return new Font("Times New Roman", Font.PLAIN, (height - 10) );
    }

    /**
     * 生成随机颜色
     * @return  随机颜色对象
     */
    private static Color randomColor(){
        Random random = new Random();
        return new Color(random.nextInt(256), random.nextInt(256), random.nextInt(256));
    }

    /**
     * 绘制干扰线
     * @param g Graphics2D对象
     */
    private static void drawLine(Graphics2D g){
        g.setColor(randomColor());
        //  (起始点x， 起始点y， 偏移量x，偏移量y)
        g.drawLine(
                random.nextInt(width),
                random.nextInt(height),
                random.nextInt(width),
                random.nextInt(height)
        );
    }

    /**
     * 绘制噪点
     * @param g Graphics2D对象
     */
    private static void drawPoint(Graphics2D g){
        g.setColor(randomColor());
        //  (起始点x， 起始点y， 宽，高)
        g.fillRect(
                random.nextInt(width),
                random.nextInt(height),
                2,
                2
        );
    }

    /**
     *  绘制验证码
     * @param g Graphics2D对象
     * @param verCode   验证码
     */
    private static void drawCode(Graphics2D g, String verCode){
        //  验证码长度
        int codeCount = verCode.length();
        //  坐标x
        int x = width / codeCount - 2;
        //  坐标y
        int y = height - 10;
        //  随机产生codeCount个字符的验证码。
        for (int i = 0; i < codeCount; i++) {
            int x1 = x * (i +1);
            //  获取当前需要绘制的字符
            String strRandom = String.valueOf(verCode.charAt(i));
            //  设置随机颜色
            g.setColor(randomColor());
            g.translate(random.nextInt(3), random.nextInt(3));
            //  设置字体旋转角度，角度小于30度
            int degree = random.nextInt() % 10;
            //  正向旋转
            g.rotate(degree * Math.PI / 180);
            //  绘制验证码
            g.drawString(strRandom, x1, y);
            //  反向旋转
            g.rotate(-degree * Math.PI / 180);
        }
    }

    /**
     * 生成验证码
     * @param length    长度
     * @return  验证码
     */
    private static String createCode(Integer length){
        String allCode = "0123456789abcdefghijklmnopqrstuvwxwzABCDEFGHIJKLMNOPQRSTUVWXYZ";
        StringBuilder codeSb = new StringBuilder();
        int randomNextLength = allCode.length();
        for (int i = 0; i < length; i++){
            codeSb.append(allCode.charAt(random.nextInt(randomNextLength)));
        }
        return codeSb.toString();
    }

}
